﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SportsStore.Domain.Entities;
using SportsStore.Domain.Abstract;
using SportsStore.WebUI.Controllers;
using System.Web.Mvc;
using SportsStore.WebUI.Models;
using SportsStore.WebUI.HtmlHelpers;

namespace SportsStore.Test 
{
    [TestClass]
    public class CartTest 
    {
        [TestMethod]
        public void Can_Add_New_Lines()
        {
            //Arrange some products
             Product P1 = new Product {ProductID = 1, Name = "P1"};
             Product P2 = new Product { ProductID = 2, Name = "P2" };
 
            //Arrange Create new cart
             Cart target = new Cart();

            //Act
            target.AddItem(P1, 2);
            target.AddItem(P2, 5);
            Cartline[] results = target.Lines.ToArray();
 
            //Assert
            Assert.AreEqual(results.Length, 2);
            Assert.AreEqual(results[0].Product, P1);
            Assert.AreEqual(results[1].Product, P2);
        }

        [TestMethod]
        public void Can_Add_Quantity_For_Existing_Lines()
        {
            //Arrange some products
            Product P1 = new Product { ProductID = 1, Name = "P1" };
            Product P2 = new Product { ProductID = 2, Name = "P2" };

            //Arrange Create new cart
            Cart target = new Cart();

            //Act
            target.AddItem(P1, 2);
            target.AddItem(P2, 5);
            target.AddItem(P1, 10);
            Cartline[] results = target.Lines.OrderBy(l => l.Product.ProductID).ToArray();

            //Assert
            Assert.AreEqual(results.Length, 2);
            Assert.AreEqual(results[0].Quantity, 12);
            Assert.AreEqual(results[1].Quantity, 5);
        }

        [TestMethod]
        public void Can_Remove_Lines()
        {
            //Arrange some products
            Product P1 = new Product { ProductID = 1, Name = "P1" };
            Product P2 = new Product { ProductID = 2, Name = "P2" };
            Product P3 = new Product { ProductID = 3, Name = "P3" };

            //Arrange Create new cart
            Cart target = new Cart();

            //Add products
            target.AddItem(P1, 2);
            target.AddItem(P2, 5);
            target.AddItem(P3, 10);

            //Remove Products
            target.RemoveLine(P1);

            //Assert
            Assert.AreEqual(target.Lines.Count(), 2);
            Assert.AreEqual(target.Lines.Where(l => l.Product == P1).Count(), 0);            
        }

        [TestMethod]
        public void Calculate_Cart_Total() 
        {
            //Arrange some products
            Product P1 = new Product { ProductID = 1, Name = "P1", Price= 100M };
            Product P2 = new Product { ProductID = 2, Name = "P2", Price = 10M };
            Product P3 = new Product { ProductID = 3, Name = "P3", Price = 50M };

            //Arrange Create new cart
            Cart target = new Cart();

            //Add products
            target.AddItem(P1, 2);
            target.AddItem(P2, 5);
            target.AddItem(P3, 10);

            decimal result = target.ComputeTotalValue();

            //Assert
            Assert.AreEqual(result, 750);            
        }

        [TestMethod]
        public void Can_Clear_CartLine()
        {
            //Arrange some products
            Product P1 = new Product { ProductID = 1, Name = "P1", Price = 100M };
            Product P2 = new Product { ProductID = 2, Name = "P2", Price = 10M };
            Product P3 = new Product { ProductID = 3, Name = "P3", Price = 50M };

            //Arrange Create new cart
            Cart target = new Cart();

            //Add products
            target.AddItem(P1, 2);
            target.AddItem(P2, 5);
            target.AddItem(P3, 10);

            target.Clear();

            //Assert
            Assert.AreEqual(target.Lines.Count(), 0);
        }

        [TestMethod]
        public void Can_Add_To_Cart()
        {
            //Create mock repository
            Mock<IProductRepository> mock = new Mock<IProductRepository>();
            //Arrange some products.
            mock.Setup(m => m.Products).Returns(new Product[]{            
            new Product { ProductID = 1, Name = "P1", Category="Apples",  Price = 100M },
            new Product { ProductID = 2, Name = "P2", Category="Apples", Price = 10M },
            new Product { ProductID = 3, Name = "P3", Category="Juse", Price = 50M }
            }.AsQueryable());

            //Arrange Create new cart
            Cart c = new Cart();

            //Create controller.
            CartController target = new CartController(mock.Object, null);

            //Add products to cart.
            target.AddToCart(c, 1, null);

            //Assert
            Assert.AreEqual(c.Lines.Count(), 1);
            Assert.AreEqual(c.Lines.ToArray()[0].Product.ProductID, 1);
        }

        [TestMethod]
        public void Adding_Product_Goes_To_Cart_Screen()
        {
            //Create mock repository
            Mock<IProductRepository> mock = new Mock<IProductRepository>();
            //Arrange some products.
            mock.Setup(m => m.Products).Returns(new Product[]{            
            new Product { ProductID = 1, Name = "P1", Category="Apples",  Price = 100M },
            new Product { ProductID = 2, Name = "P2", Category="Apples", Price = 10M },
            new Product { ProductID = 3, Name = "P3", Category="Juse", Price = 50M }
            }.AsQueryable());

            //Arrange Create new cart
            Cart c = new Cart();

            //Create controller.
            CartController target = new CartController(mock.Object, null);

            //Add products to cart.
            RedirectToRouteResult result = target.AddToCart(c, 1, "myreturnUrl");

            //Assert
            Assert.AreEqual(result.RouteValues["action"], "Index");
            Assert.AreEqual(result.RouteValues["returnUrl"], "myreturnUrl");
        }

        [TestMethod]
        public void Can_View_Cart_Content()
        {
            //Arrange a cart
            Cart c = new Cart();

            //Arrange cart controller
            CartController target = new CartController(null, null);

            //Act -- call index method..
            CartIndexViewModel result = (CartIndexViewModel)target.Index(c, "myUrl").ViewData.Model;

            //Assert
            Assert.AreSame(result.cart, c);
            Assert.AreEqual(result.ReturnUrl, "myUrl");
        }

        [TestMethod] 
        public void Cannot_Checkout_Empty_Cart() { 
 
            // Arrange - create a mock order processor 
            Mock<IOrderProcessor> mock = new Mock<IOrderProcessor>(); 
            // Arrange - create an empty cart 
            Cart cart = new Cart(); 
            // Arrange - create shipping details 
            ShippingDetails shippingDetails = new ShippingDetails(); 
            // Arrange - create an instance of the controller 
            CartController target = new CartController(null, mock.Object); 
 
            // Act 
            ViewResult result = target.Checkout(cart, shippingDetails); 
 
            // Assert - check that the order hasn't been passed on to the processor
            mock.Verify(m => m.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()),
                Times.Never()); 
            // Assert - check that the method is returning the default view 
            Assert.AreEqual("", result.ViewName); 
            // Assert - check that we are passing an invalid model to the view 
            Assert.AreEqual(false, result.ViewData.ModelState.IsValid); 
        }


        [TestMethod]
        public void Cannot_Checkout_Invalid_ShippingDetails()
        {

            // Arrange - create a mock order processor 
            Mock<IOrderProcessor> mock = new Mock<IOrderProcessor>();
            // Arrange - create a cart with an item 
            Cart cart = new Cart();
            cart.AddItem(new Product(), 1);

            // Arrange - create an instance of the controller 
            CartController target = new CartController(null, mock.Object);
            // Arrange - add an error to the model 
            target.ModelState.AddModelError("error", "error");

            // Act - try to checkout 
            ViewResult result = target.Checkout(cart, new ShippingDetails());

            // Assert - check that the order hasn't been passed on to the processor 
            mock.Verify(m => m.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()),
                Times.Never());
            // Assert - check that the method is returning the default view 
            Assert.AreEqual("", result.ViewName);
            // Assert - check that we are passing an invalid model to the view 
            Assert.AreEqual(false, result.ViewData.ModelState.IsValid);
        }

        [TestMethod]
        public void Can_Checkout_And_Submit_Order()
        {
            // Arrange - create a mock order processor 
            Mock<IOrderProcessor> mock = new Mock<IOrderProcessor>();
            // Arrange - create a cart with an item 
            Cart cart = new Cart();
            cart.AddItem(new Product(), 1);
            // Arrange - create an instance of the controller 
            CartController target = new CartController(null, mock.Object);

            // Act - try to checkout 
            ViewResult result = target.Checkout(cart, new ShippingDetails());

            // Assert - check that the order has been passed on to the processor 
            mock.Verify(m => m.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()),
                Times.Once());
            // Assert - check that the method is returning the Completed view 
            Assert.AreEqual("Completed", result.ViewName);
            // Assert - check that we are passing a valid model to the view 
            Assert.AreEqual(true, result.ViewData.ModelState.IsValid);
        } 
    }
}
